package spider.impl.download;

import spider.Configguration.Configuration;
import spider.NovelSiteEnum;
import spider.entitys.Chapter;
import spider.entitys.ChapterDetail;
import spider.interfaces.IChapterDetailSpider;
import spider.interfaces.IChapterSpider;
import spider.interfaces.INovelDownload;
import spider.util.ChapterDetailSpiderFactory;
import spider.util.ChapterSpiderFactory;
import spider.util.NovelSpiderUtil;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.*;
import java.util.concurrent.*;

/**
 * created by yb
 * on 2017/8/10.
 * 多线程实现多线程下载任意网站的小说
 * 1.得到给网站的小说所有章节
 * 2.通过计算,将这些章节分配给指定数量的线程,让他们去解析,并保存到文本文件中
 * 3.通知主线程,将这些零散的小文件组合成一个大文件,最后删除那些小文件
 **/
public class NovelDownload implements INovelDownload {
    @Override
    public String dowload(String url, Configuration config) {
        IChapterSpider chapterSpider = ChapterSpiderFactory.GetChapterSpider(url);
        List<Chapter> chapters = chapterSpider.getChapter(url);
        // 某个线程下载完毕后,需通知主线程下载好了
        // 所有线程都下载好了,合并
        int size = config.getSize();
        int maxThreadSize = (chapters.size() + size - 1) / size;
        Map<String, List<Chapter>> downLoadTaskAlloc = new HashMap<>(); //下载任务分配
        for (int i = 0; i < maxThreadSize; i++) {
            int fromIndex = i * config.getSize();
            int toIndex = fromIndex + config.getSize() - 1;
            toIndex = toIndex >= chapters.size() ? chapters.size() - 1 : toIndex;
            downLoadTaskAlloc.put(fromIndex + "-" + toIndex, chapters.subList(fromIndex, toIndex + 1));
        }
        ExecutorService service = Executors.newFixedThreadPool(maxThreadSize);
        Set<String> keyset = downLoadTaskAlloc.keySet();
        List<Future<String>> tasks = new ArrayList<>();

        //创建缺失的路径
        String savePath = config.getPath() + "/" + NovelSiteEnum.getNovelSiteEnumByUrl(url).getUrl();
        new File(savePath).mkdirs();

        for (String key : keyset) {
            tasks.add(service.submit(new DownloadCallable(savePath + "/" + key + ".txt", downLoadTaskAlloc.get(key),config.getTryTimes())));
        }
        service.shutdown();
        for (Future<String> future : tasks) {
            try {
                System.out.println(future.get() + ",下载完成");
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }
        }
        NovelSpiderUtil.multiFileMerger(savePath,null,true);
        return savePath;
    }

}

class DownloadCallable implements Callable<String> {
    private List<Chapter> chapters;
    private String path;
    private int tryTime;
    public DownloadCallable(String path, List<Chapter> chapters,int tryTime) {
        this.path = path;
        this.chapters = chapters;
        this.tryTime = tryTime;
    }

    /**
     *设置下载的地点,具体信息等  返回下载路径
     */
    @Override
    public String call() throws Exception {
        try (PrintWriter out = new PrintWriter(new File(path), "UTF-8");) {
            for (Chapter chapter : chapters) {
                IChapterDetailSpider spider = ChapterDetailSpiderFactory.GetChapterDetailSpider(chapter.getUrl());
                ChapterDetail chapterDetail = null;
                for(int i = 0;i < tryTime;i++){
                    try{
                        chapterDetail = spider.getChapterDetail(chapter.getUrl());
                        spider.getChapterDetail(chapter.getUrl());
                        out.println(chapterDetail.getTittle());
                        out.println(chapterDetail.getContent());
                        break;
                    }catch (Exception e){
                        System.out.println(chapter.getUrl()+"尝试["+(i+1)+"/"+tryTime+"]次下载");
                    }
                }
            }

        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return path;
    }
}
